import paramiko
import time
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed

logger = logging.getLogger("fast-run")


def apply_certificate_by_webroot(domain, hosts, primary_host=None, user="root", password=None, email=None,
                      webroot_path="/var/www/certbot"):
    """
    为域名申请 Let's Encrypt 证书（webroot模式）
    适用于域名指向多个服务器的情况

    Args:
        domain (str): 要申请证书的域名
        hosts (list): 所有服务器IP列表，如 ["192.168.1.100", "192.168.1.101"]
        primary_host (str): 主服务器IP，用于申请和存储证书。如果为None，使用hosts[0]
        user (str): SSH用户名，默认root
        password (str): SSH密码，如果为None则使用SSH密钥认证
        email (str): Let's Encrypt邮箱，如果为None则自动生成
        webroot_path (str): webroot目录路径，默认/var/www/certbot

    Returns:
        dict: 成功时返回证书内容 {'cert': '...', 'privkey': '...', 'fullchain': '...'}
        None: 失败时返回None

    Example:
        result = apply_certificate(
            domain="example.com",
            hosts=["192.168.1.100", "192.168.1.101", "192.168.1.102"],
            primary_host="192.168.1.100",
            password="your_password"
        )
    """
    if not domain or not hosts:
        logger.error("域名和服务器列表不能为空")
        return None

    if primary_host is None:
        primary_host = hosts[0]
        logger.info(f"未指定主服务器，使用第一个服务器: {primary_host}")

    if primary_host not in hosts:
        logger.error(f"主服务器 {primary_host} 不在服务器列表中")
        return None

    if email is None:
        email = f"admin@{domain}"
        logger.info(f"使用默认邮箱: {email}")

    logger.info(f"开始为域名 {domain} 申请证书")
    logger.info(f"服务器列表: {hosts}")
    logger.info(f"主服务器: {primary_host}")

    try:
        # 第1步: 在所有服务器上准备webroot
        success_hosts = _prepare_webroot_on_hosts(hosts, webroot_path, user, password)

        if not success_hosts:
            logger.error("所有服务器webroot准备都失败了")
            return None

        logger.info(f"webroot准备成功的服务器: {success_hosts}")

        if primary_host not in success_hosts:
            logger.error(f"主服务器 {primary_host} webroot准备失败")
            return None

        # 第2步: 在主服务器上申请证书
        cert_data = _request_certificate(primary_host, domain, webroot_path, email, user, password)

        if cert_data:
            logger.info(f"✅ 域名 {domain} 证书申请成功")
            return cert_data
        else:
            logger.error(f"❌ 域名 {domain} 证书申请失败")
            return None

    except Exception as e:
        logger.error(f"证书申请过程中发生异常: {e}")
        return None


def _prepare_webroot_on_hosts(hosts, webroot_path, user, password):
    """在所有服务器上并行准备webroot目录和nginx配置"""

    def prepare_single_host(host):
        try:
            logger.info(f"正在准备 {host} 的webroot...")

            ssh = _connect_ssh(host, user, password)
            if not ssh:
                return False

            # 1. 创建webroot目录
            commands = [
                f"sudo mkdir -p {webroot_path}",
                f"sudo chmod 755 {webroot_path}",
                f"sudo chown -R www-data:www-data {webroot_path} 2>/dev/null || sudo chown -R nginx:nginx {webroot_path} 2>/dev/null || true"
            ]

            for cmd in commands:
                ssh.exec_command(cmd)

            # 2. 检查并安装certbot
            stdin, stdout, stderr = ssh.exec_command("which certbot")
            if not stdout.read().decode().strip():
                logger.info(f"在 {host} 上安装certbot...")
                ssh.exec_command("sudo apt-get update -y && sudo apt-get install -y certbot")

            # 3. 配置nginx的acme-challenge location
            acme_config = f'''server {{
                listen 80;
                server_name _;

                location ^~ /.well-known/acme-challenge/ {{
                    default_type "text/plain";
                    alias {webroot_path}/;
                    try_files $uri =404;
                }}
            }}'''

            # 创建nginx配置文件
            ssh.exec_command(f"echo '{acme_config}' | sudo tee /etc/nginx/conf.d/acme-challenge.conf > /dev/null")

            # 4. 测试并重载nginx配置
            stdin, stdout, stderr = ssh.exec_command("sudo nginx -t")
            nginx_test = stderr.read().decode()

            if "successful" in nginx_test or "syntax is ok" in nginx_test:
                ssh.exec_command("sudo nginx -s reload")
                logger.info(f"✅ {host} nginx配置更新成功")
            else:
                logger.warning(f"⚠️  {host} nginx配置测试失败，但继续执行: {nginx_test}")

            # 5. 测试webroot目录写入权限
            test_file = f"{webroot_path}/.test-{int(time.time())}"
            ssh.exec_command(f"echo 'test' | sudo tee {test_file} > /dev/null")
            stdin, stdout, stderr = ssh.exec_command(f"sudo test -f {test_file} && echo 'OK' || echo 'FAIL'")
            test_result = stdout.read().decode().strip()
            ssh.exec_command(f"sudo rm -f {test_file}")

            ssh.close()

            if test_result == "OK":
                logger.info(f"✅ {host} webroot准备完成")
                return True
            else:
                logger.error(f"❌ {host} webroot写入测试失败")
                return False

        except Exception as e:
            logger.error(f"❌ {host} webroot准备异常: {e}")
            return False

    # 并行处理所有服务器
    success_hosts = []
    max_workers = min(len(hosts), 10)  # 最多10个并发

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        future_to_host = {executor.submit(prepare_single_host, host): host for host in hosts}

        for future in as_completed(future_to_host, timeout=180):  # 3分钟超时
            host = future_to_host[future]
            try:
                if future.result():
                    success_hosts.append(host)
            except Exception as e:
                logger.error(f"{host} webroot准备任务异常: {e}")

    return success_hosts


def _request_certificate(host, domain, webroot_path, email, user, password):
    """在指定服务器上申请证书"""
    try:
        logger.info(f"在 {host} 上申请 {domain} 的证书...")

        ssh = _connect_ssh(host, user, password)
        if not ssh:
            return None

        # 构建certbot命令
        cert_cmd = (
            f"sudo certbot certonly "
            f"--webroot -w {webroot_path} "
            f"-d {domain} "
            f"--non-interactive "
            f"--agree-tos "
            f"-m {email} "
            f"--preferred-challenges http "
            f"--force-renewal "
            f"--no-eff-email"
        )

        logger.info(f"执行命令: {cert_cmd}")

        # 执行certbot命令
        stdin, stdout, stderr = ssh.exec_command(cert_cmd, timeout=300)  # 5分钟超时
        exit_status = stdout.channel.recv_exit_status()

        stdout_output = stdout.read().decode()
        stderr_output = stderr.read().decode()

        # 记录详细输出
        if stdout_output:
            logger.info(f"certbot stdout: {stdout_output}")
        if stderr_output:
            logger.info(f"certbot stderr: {stderr_output}")

        ssh.close()

        # 判断是否成功
        success_indicators = [
            "Congratulations",
            "Certificate is saved",
            "Successfully received certificate"
        ]

        is_success = (exit_status == 0 or
                      any(indicator in stdout_output + stderr_output for indicator in success_indicators))

        if is_success:
            logger.info(f"✅ {domain} 证书申请成功")
            # 读取证书文件
            return _read_certificate_files(host, domain, user, password)
        else:
            logger.error(f"❌ {domain} 证书申请失败，退出码: {exit_status}")
            return None

    except Exception as e:
        logger.error(f"在 {host} 申请证书时发生异常: {e}")
        return None


def _read_certificate_files(host, domain, user, password):
    """从服务器读取证书文件内容"""
    try:
        logger.info(f"从 {host} 读取 {domain} 的证书文件...")

        ssh = _connect_ssh(host, user, password)
        if not ssh:
            return None

        cert_dir = f"/etc/letsencrypt/live/{domain}"
        cert_files = {
            'cert': f"{cert_dir}/cert.pem",
            'privkey': f"{cert_dir}/privkey.pem",
            'fullchain': f"{cert_dir}/fullchain.pem",
            'chain': f"{cert_dir}/chain.pem"
        }

        cert_data = {}

        # 检查证书目录是否存在
        stdin, stdout, stderr = ssh.exec_command(f"sudo test -d {cert_dir} && echo 'EXISTS' || echo 'NOT_FOUND'")
        dir_check = stdout.read().decode().strip()

        if dir_check != "EXISTS":
            logger.error(f"证书目录不存在: {cert_dir}")
            ssh.close()
            return None

        # 读取每个证书文件
        for key, file_path in cert_files.items():
            stdin, stdout, stderr = ssh.exec_command(f"sudo cat {file_path} 2>/dev/null")
            content = stdout.read().decode()

            if content and content.strip():
                cert_data[key] = content
                logger.info(f"✅ 读取 {key}: {len(content)} 字符")
            else:
                if key in ['cert', 'privkey', 'fullchain']:  # 必需文件
                    logger.error(f"❌ 无法读取必需文件: {file_path}")
                    ssh.close()
                    return None
                else:
                    logger.warning(f"⚠️  可选文件读取失败: {file_path}")
                    cert_data[key] = None

        ssh.close()

        # 验证证书内容
        if cert_data.get('cert') and cert_data.get('privkey') and cert_data.get('fullchain'):
            # 简单验证证书格式
            if ('BEGIN CERTIFICATE' in cert_data['cert'] and
                    'BEGIN PRIVATE KEY' in cert_data['privkey']):
                logger.info(f"✅ {domain} 证书读取完成，证书有效")
                return cert_data
            else:
                logger.error("❌ 证书格式验证失败")
                return None
        else:
            logger.error("❌ 证书文件不完整")
            return None

    except Exception as e:
        logger.error(f"读取证书文件时发生异常: {e}")
        return None


def _connect_ssh(host, user, password):
    """建立SSH连接"""
    try:
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        connect_kwargs = {
            'hostname': host,
            'username': user,
            'timeout': 10,
            'banner_timeout': 10
        }

        if password:
            connect_kwargs['password'] = password
        else:
            # 使用SSH密钥认证
            connect_kwargs['look_for_keys'] = True
            connect_kwargs['allow_agent'] = True

        ssh.connect(**connect_kwargs)
        return ssh

    except paramiko.ssh_exception.AuthenticationException:
        logger.error(f"SSH认证失败: {host}")
        return None
    except paramiko.ssh_exception.SSHException as e:
        logger.error(f"SSH连接异常 {host}: {e}")
        return None
    except Exception as e:
        logger.error(f"无法连接到 {host}: {e}")
        return None


# 便捷函数：只需要域名和服务器列表
def get_certificate(domain, servers, primary_server=None, ssh_user="root", ssh_password=None):
    """
    便捷函数：为域名申请SSL证书

    Args:
        domain: 域名，如 "example.com"
        servers: 服务器IP列表，如 ["1.1.1.1", "2.2.2.2"]
        primary_server: 主服务器IP，如果不指定则使用第一个
        ssh_user: SSH用户名，默认root
        ssh_password: SSH密码，如果不提供则使用密钥认证

    Returns:
        dict 或 None: 证书内容字典或失败返回None

    Usage:
        cert = get_certificate("example.com", ["1.1.1.1", "2.2.2.2"])
        if cert:
            print("证书申请成功！")
            # 保存证书文件
            with open(f"{domain}.crt", "w") as f:
                f.write(cert['fullchain'])
            with open(f"{domain}.key", "w") as f:
                f.write(cert['privkey'])
    """
    return apply_certificate(
        domain=domain,
        hosts=servers,
        primary_host=primary_server,
        user=ssh_user,
        password=ssh_password
    )